package com.gitee.wsl.data.state.repo

import com.gitee.wsl.data.state.KalugaState
import com.gitee.wsl.data.state.defaultLazySharedFlow
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.flow.MutableSharedFlow
import kotlin.coroutines.CoroutineContext

/**
 * A [BaseColdStateRepo] that represents its [State] as a Cold flow. Data will only be set when the state is observed.
 *
 * This class uses a very simple initialize and deinitialize pattern without changes of state by default
 *
 * This implementation uses a [MutableSharedFlow]. If you want to use a cold state repo based on StateFlow,
 * consider [ColdStateFlowRepo]
 * @param State the type of [KalugaState] represented by this repo.
 * @param coroutineContext the [CoroutineContext] used to create a coroutine scope for this state machine. Make sure that if you pass a coroutine context that has sequential execution if you do not want simultaneous state changes. The default Main dispatcher meets these criteria.
 * @param lazyMutableFlow A [Lazy] [MutableSharedFlow] of [State] in which to store the backing data
 * @see [BaseColdStateRepo]
 */
abstract class ColdStateRepo<State : KalugaState>(
    coroutineContext: CoroutineContext = Dispatchers.Main.immediate,
    override val lazyMutableFlow: Lazy<MutableSharedFlow<State>> = defaultLazySharedFlow(),
) : BaseColdStateRepo<State, MutableSharedFlow<State>>(coroutineContext) {

    final override suspend fun firstCollection() {
        initialize()
    }

    final override suspend fun laterCollections() = takeAndChangeState {
        {
            initialValue()
        }
    }

    final override suspend fun noMoreCollections(): State = takeAndChangeState {
        deinitialize(it)
        it.remain()
    }

    /**
     * Called when the repo is no longer observed
     * @param state The last [State] before observation stopped.
     */
    abstract suspend fun deinitialize(state: State)
}